package tenglang.common.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * Redis缓存配置类
 * @author szekinwin
 *
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{

	@Bean
    public KeyGenerator KeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

	

    //缓存管理器
    @Bean 
    @ConditionalOnClass(ObjectMapper.class)
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory,ObjectMapper objectMapper) {
    	
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer=this.jackson2JsonRedisSerializer(objectMapper);
        
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        
         //注意此处，每设置一次属性生成一个新的配置对象
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
        		         .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
        		         .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                         .entryTtl(Duration.ofSeconds(30)).disableCachingNullValues();
//        defaultCacheConfig.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));
//        defaultCacheConfig.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
      //设置默认超过期时间是30秒

        //设置缓存过期时间 
        Map<String, Long> expires = new HashMap<String, Long>();
        /*//根据value设置缓存过期时间（秒）
        expires.put("BASIC_DIC", 1 * 3600L); //小时数据，24小时过期
        
        cacheManager.setExpires(expires);
        //设置缓存默认过期时间（秒）
        cacheManager.setDefaultExpiration(3600L);*/
        
//        RedisCacheManager redisCacheManager= RedisCacheManager.builder(redisCacheWriter).cacheDefaults(defaultCacheConfig)
//                .build();
        return new RedisCacheManager(redisCacheWriter,this.getRedisCacheConfigurationWithTtl(30,objectMapper),getRedisCacheConfigurationMap(objectMapper));
    }


    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(ObjectMapper objectMapper) {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        //SsoCache和BasicDataCache进行过期时间配置
        redisCacheConfigurationMap.put("SsoCache", this.getRedisCacheConfigurationWithTtl(24*60*60,objectMapper));
        redisCacheConfigurationMap.put("BasicDataCache", this.getRedisCacheConfigurationWithTtl(30*60,objectMapper));
        return redisCacheConfigurationMap;
    }



    
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
        StringRedisTemplate template = new StringRedisTemplate(factory);
        template.setValueSerializer(jackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
    
    @Bean
    @ConditionalOnClass(ObjectMapper.class)
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory,ObjectMapper objectMapper){
        StringRedisTemplate template = new StringRedisTemplate(factory);
        template.setValueSerializer(jackson2JsonRedisSerializer(objectMapper));
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public RedisCache redisCache(){
        return new RedisCache();
    }
    
    @Bean(name="redisTemplateObject")
    @ConditionalOnClass(ObjectMapper.class)
    public RedisTemplate<String,Object> redisTemplateObject(RedisConnectionFactory factory,ObjectMapper objectMapper){
    	RedisTemplate<String, Object> redisTemplate=new RedisTemplate<String, Object>();
    	redisTemplate.setConnectionFactory(factory);
    	redisTemplate.setValueSerializer(jackson2JsonRedisSerializer(objectMapper));
    	RedisSerializer<String> redisSerializer = new StringRedisSerializer();
    	redisTemplate.setKeySerializer(redisSerializer);
    	redisTemplate.setHashKeySerializer(redisSerializer);
    	redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
    	redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
    	redisTemplate.afterPropertiesSet();
    	return redisTemplate;
    }

     public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer() {
		 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
         ObjectMapper om = new ObjectMapper();
         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         jackson2JsonRedisSerializer.setObjectMapper(om);
         return jackson2JsonRedisSerializer;
	 }
     
     public GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer(ObjectMapper objectMapper) {
		 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
		 ObjectMapper objectMapperCp=objectMapper.copy();
		 objectMapperCp.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		 objectMapperCp.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         jackson2JsonRedisSerializer.setObjectMapper(objectMapperCp);
         GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer=new GenericJackson2JsonRedisSerializer(objectMapperCp); 
         return genericJackson2JsonRedisSerializer;
	 }


    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds,ObjectMapper objectMapper) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapperCp=objectMapper.copy();
        objectMapperCp.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapperCp.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapperCp);

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(jackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));
        return redisCacheConfiguration;
    }

}
